/** \file
* Implementation of the generic XML source class.
* This file is part of Chorus, available at
* http://code.google.com/p/chorus-twitter/ . This file and all associated files
* and derivative works are bound by the terms of the LGPL 3.0 license,
* available at http://www.gnu.org/licenses/lgpl-3.0-standalone.html .
* \par Subversion URL:
* \$URL: http://chorus-twitter.googlecode.com/svn/branches/cpp/src/xml/XmlSource.cpp $
* \version \$Rev: 153 $
* \date    \$Date: 2010-04-06 07:43:42 +0000 (Tue, 06 Apr 2010) $
*/

#include "config.h"

#include <stdarg.h>

#include "system/DebugConsole.hpp"
#include "xml/XmlSource.hpp"

using namespace std;


// Based off the xmlSAXHandler struct in <libxml/parser.h>
const xmlSAXHandler XmlSource::sax =
{
	0,					//internalSubsetSAXFunc internalSubset;
	0,					//isStandaloneSAXFunc isStandalone;
	0,					//hasInternalSubsetSAXFunc hasInternalSubset;
	0,					//hasExternalSubsetSAXFunc hasExternalSubset;
	0,					//resolveEntitySAXFunc resolveEntity;
	0,					//getEntitySAXFunc getEntity;
	0,					//entityDeclSAXFunc entityDecl;
	0,					//notationDeclSAXFunc notationDecl;
	0,					//attributeDeclSAXFunc attributeDecl;
	0,					//elementDeclSAXFunc elementDecl;
	0,					//unparsedEntityDeclSAXFunc unparsedEntityDecl;
	0,					//setDocumentLocatorSAXFunc setDocumentLocator;
	0,					//startDocumentSAXFunc startDocument;
	0,					//endDocumentSAXFunc endDocument;
	saxStartElement,	//startElementSAXFunc startElement;
	saxEndElement,		//endElementSAXFunc endElement;
	0,					//referenceSAXFunc reference;
	saxCharacters,		//charactersSAXFunc characters;
	0,					//ignorableWhitespaceSAXFunc ignorableWhitespace;
	0,					//processingInstructionSAXFunc processingInstruction;
	0,					//commentSAXFunc comment;
	saxMessage,			//warningSAXFunc warning;
	saxMessage,			//errorSAXFunc error;
	0,					//fatalErrorSAXFunc fatalError; /* unused error() get all the errors */
	0,					//getParameterEntitySAXFunc getParameterEntity;
	0,					//cdataBlockSAXFunc cdataBlock;
	0,					//externalSubsetSAXFunc externalSubset;
	0,					//unsigned int initialized;
						///* The following fields are extensions available only on version 2 */
	0,					//void *_private;
	0,					//startElementNsSAX2Func startElementNs;
	0,					//endElementNsSAX2Func endElementNs;
	0					//xmlStructuredErrorFunc serror;
};

XmlSource::XmlSource(XmlTree *treeInit, const std::string &pathInit) :
	tree(treeInit),
	path(pathInit)
{
	context = 0;
}

XmlSource::~XmlSource()
{
	if (context)
	{
		if (context->myDoc)
		{
			xmlFreeDoc(context->myDoc);
			context->myDoc = 0;
		}
		xmlFreeParserCtxt(context);
		context = 0;
	}
}

bool XmlSource::fetch()
{
	PDEBUG2("fetch started - ", path);
	if (context)
		return false;

	bool success = false;
	xmlDoc *newdoc = 0;
	xmlParserErrors error;

	context = xmlCreatePushParserCtxt((xmlSAXHandler*)&sax, this, 0,0,0);
	if (!context)
	{
		PDEBUG_XML("xmlCreatePushParserCtxt failed");
		goto fail;
	}

	if (!perform())
		goto fail;

	newdoc = context->myDoc;

	error = (xmlParserErrors)xmlParseChunk(context, 0,0,true);
	if (error != XML_ERR_OK)
	{
		PDEBUG_XML("xmlParseChunk failed");
		goto fail;
	}

	success = true;
	PDEBUG2("fetch completed - ", path);
	fail:
	if (context)
	{
		xmlFreeParserCtxt(context);
		context = 0;
	}
	if (newdoc)
		xmlFreeDoc(newdoc);

	return success;
}

bool XmlSource::writeData(const char *buffer, size_t size)
{
	const xmlParserErrors error = (xmlParserErrors)xmlParseChunk(context,
		buffer, size, 0);
	if (error != XML_ERR_OK)
	{
		PDEBUG_XML("xmlParseChunk failed");
		return false;
	}
	return true;
}

void XmlSource::saxStartElement(void *ctx, const xmlChar *name,
	const xmlChar **attrs)
{
	((XmlSource*)ctx)->tree->startElement(name, attrs);
}

void XmlSource::saxEndElement(void *ctx, const xmlChar *name)
{
	((XmlSource*)ctx)->tree->endElement(name);
}

void XmlSource::saxCharacters(void *ctx, const xmlChar *chconst, int len)
{
	xmlChar *ch = (xmlChar*)chconst;
	while (*ch <= ' ')	
	{
		ch++;
		if (!--len)
			return;
	}

	((XmlSource*)ctx)->tree->elementText(ch, len);
}

void XmlSource::saxMessage(void *ctx, const char *msg, ...)
{
	va_list vargs;
	va_start(vargs, msg);
	char buffer[80];
	vsnprintf(buffer, sizeof(buffer), msg, vargs);
	PDEBUG2("SAX error handler invoked:", buffer);
	va_end(vargs);
}
